﻿// ffplayer-update.cpp: 定义应用程序的入口点。
//

#include "ffplayer-update.h"

class AppException : public std::runtime_error {
public:
    explicit AppException(const char* message);
};

AppException::AppException(const char* message) : std::runtime_error(message) {
}

std::string parentDir(const std::string& path) {
    std::size_t pos = path.find_last_of("\\/");
    if (pos != std::string::npos) {
        if (pos == 2) {
            //根目录下的目录
            return path.substr(0, pos + 1);
        }
        else if (pos == 0) {
            //相对目录的根目录，比如\\aa
            return path.substr(0, 1);
        }
        else {
            return path.substr(0, pos);
        }
    }
    return path;
}

bool exist(const char* file) {
    struct stat info {};
    return stat(file, &info) == 0;
}

void mkdirs(const std::string& path) {
    if (exist(path.c_str())) {
        return;
    }
    std::list<std::string> list;
    list.push_front(path);
    std::string dir = parentDir(path);
    while (!exist(dir.c_str())) {
        list.push_front(dir);
        dir = parentDir(dir);
    }
    for (auto& it : list) {
        if (_mkdir(it.c_str())) {
            int er;
            _get_errno(&er);
            if (EEXIST != er) {
                char buffer[MAX_PATH] = { 0 };
                char msg[MAX_PATH] = { 0 };
                strerror_s(msg, er);
                snprintf(buffer, MAX_PATH, "创建目录%s失败：%s", it.c_str(), msg);
                throw AppException(buffer);
            }
        }
    }
}

bool copy(const char* source, const char* target) {
    if (!exist(source)) {
        return false;
    }
    if (exist(target)) {
        remove(target);
    }
    std::fstream ifile(source, std::ios::in | std::ios::binary);
    if (ifile.fail()) {
        std::cout << "打开源文件[" << source << "]失败" << std::endl;
        return false;
    }
    std::string p = parentDir(target);
    mkdirs(p);
    std::fstream ofile(target, std::ios::out | std::ios::binary);
    if (ofile.fail()) {
        std::cout << "创建复制目标文件[" << target << "]失败" << std::endl;
        return false;
    }
    ofile << ifile.rdbuf();
    return true;
}

bool copyDir(const std::string& source, const std::string& target) {
    if (source.empty() || target.empty() || !exist(source.c_str())) {
        return false;
    }
    struct _finddata_t data {};
    intptr_t hnd = _findfirst((source + "\\*").c_str(), &data);
    if (hnd >= 0) {
        for (int nRet = (hnd < 0) ? -1 : 1;nRet >= 0;nRet = _findnext(hnd, &data)) {
            if (data.attrib == _A_SUBDIR) {
                if (!strcmp(".", data.name) || !strcmp("..", data.name)) {
                    continue;
                }
                char subSource[MAX_PATH] = { 0 };
                char subTarget[MAX_PATH] = { 0 };
                try {
                    sprintf_s(subSource, "%s\\%s", source.c_str(), data.name);
                    sprintf_s(subTarget, "%s\\%s", target.c_str(), data.name);
                    copyDir(subSource, subTarget);
                }
                catch (...) {
                    std::cout << "复制目录" << subSource << "失败" << std::endl;
                }
            }
            else if (data.attrib == _A_NORMAL || data.attrib & _A_HIDDEN || data.attrib & _A_SYSTEM || data.attrib & _A_ARCH) {
                char filePath[MAX_PATH] = { 0 };
                char targetFile[MAX_PATH] = { 0 };
                try {
                    sprintf_s(filePath, "%s\\%s", source.c_str(), data.name);
                    sprintf_s(targetFile, "%s\\%s", target.c_str(), data.name);
                    copy(filePath, targetFile);
                }
                catch (...) {
                    std::cout << "复制文件" << filePath << "失败" << std::endl;
                }
            }
        }
    }
    _findclose(hnd);
    return true;
}

void removes(const std::string& path) {
    if (path.empty() || !exist(path.c_str())) {
        return;
    }
    struct _finddata_t data {};
    intptr_t hnd = _findfirst((path + "\\*").c_str(), &data);
    if (hnd >= 0) {
        for (int nRet = (hnd < 0) ? -1 : 1;nRet >= 0;nRet = _findnext(hnd, &data)) {
            if (data.attrib == _A_SUBDIR) {
                if (!strcmp(".", data.name) || !strcmp("..", data.name)) {
                    continue;
                }
                char subDir[MAX_PATH] = { 0 };
                try {
                    sprintf_s(subDir, "%s\\%s", path.c_str(), data.name);
                    removes(subDir);
                }
                catch (...) {
                    std::cout << "删除目录" << subDir << "失败" << std::endl;
                }
            }
            else if (data.attrib == _A_NORMAL || data.attrib & _A_HIDDEN || data.attrib & _A_SYSTEM || data.attrib & _A_ARCH) {
                char filePath[MAX_PATH] = { 0 };
                try {
                    sprintf_s(filePath, "%s\\%s", path.c_str(), data.name);
                    remove(filePath);
                }
                catch (...) {
                    std::cout << "删除文件" << filePath << "失败" << std::endl;
                }
            }
        }
    }
    RemoveDirectoryA(path.c_str());
    _findclose(hnd);
}

std::string exePath() {
    char buf[MAX_PATH] = { 0 };
    GetModuleFileNameA(nullptr, buf, MAX_PATH - 1);
    return buf;
}

DWORD processId(const char* name) {
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        return -1;
    }
    DWORD ret = -1;
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (!Process32First(hProcessSnap, &pe32)) {
        CloseHandle(hProcessSnap);
        return -1;
    }
    do {
        if (!strcmp(name, pe32.szExeFile)) {
            ret = pe32.th32ProcessID;
            break;
        }
    } while (Process32Next(hProcessSnap, &pe32));
    CloseHandle(hProcessSnap);
    return ret;
}

void waitPlayerExist() {
    DWORD id = processId("盎宇宙全息剧本娱乐平台.exe");
    DWORD err = -1;
    while (id != err) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        id = processId("盎宇宙全息剧本娱乐平台.exe");
    }
}

void copy2target() {
    try {
        std::string target = parentDir(exePath());
        std::string source = parentDir(target) + "\\" + "update";
        copyDir(source, target);
        removes(source);
    }
    catch (...) {
    }
}

int startPlayer() {
    std::string dir = parentDir(exePath());
    std::string file = dir + "\\" + "盎宇宙全息剧本娱乐平台.exe";
    HINSTANCE inst = ShellExecuteA(nullptr, "open", file.c_str(), nullptr, dir.c_str(), SW_SHOW);
    if ((INT_PTR)inst > 32) {
        std::cout << "成功启动程序" << std::endl;
        return 0;
    }
    else {
        std::cout << "启动程序失败:" << (INT_PTR)inst << std::endl;
        return -1;
    }
}

int main() {
    waitPlayerExist();
    copy2target();
    return startPlayer();
}
